﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Net;
using XTrace;

namespace FitAutomation.FitProcess
{
    public static class NetworkExt
    {

        [DebuggerStepThrough]
        public static bool Send(this TcpClient client, byte[] buf)
        {
            try
            {
                var stream = client.GetStream();
                var bufLen = BitConverter.GetBytes((uint)buf.Length);
                Debug.Verbose("Send", "{0} bytes", buf.Length);
                stream.Write(bufLen, 0, bufLen.Length); // send the length of message
                stream.Write(buf, 0, buf.Length);
                return true;
            }
            catch (IOException ex)
            {
                Debug.Verbose("HandledException", ex);
                return false;
            }
            catch (ObjectDisposedException ex)
            {
                Debug.Verbose("HandledException", ex);
                return false;
            }
        }

        static readonly XTraceSource Trace = new XTraceSource("Network");
        static readonly XTraceSource Debug = new XTraceSource("Network Debug");

        [DebuggerStepThrough]
        public static int TryRead(this NetworkStream stream, byte[] buf)
        {
            return TryRead(stream, buf, 0, buf.Length);
        }

        [DebuggerStepThrough]
        public static int TryRead(this NetworkStream stream, byte[] buf, int offset, int size)
        {
            try
            {
                return stream.Read(buf, offset, size);
            }
            catch (IOException ex)
            {
                Debug.Verbose("HandledException", ex);
                return -1;
            }
            catch (ObjectDisposedException ex)
            {
                Debug.Verbose("HandledException", ex);
                return -1;
            }
        }

        [DebuggerStepThrough]
        public static TcpClient TryAcceptTcpClient(this TcpListener listener)
        {
            try
            {
                return listener.AcceptTcpClient();
            }
            catch (ObjectDisposedException ex)
            {
                Debug.Verbose("HandledException", ex);
                return null;
            }
            catch (SocketException ex)
            {
                Debug.Verbose("HandledException", ex);
                return null;
            }
        }

        [DebuggerStepThrough]
        public static byte[] TryReceive(this UdpClient client, ref IPEndPoint ep)
        {
            try
            {
                return client.Receive(ref ep);
            }
            catch (ObjectDisposedException ex)
            {
                Debug.Verbose("HandledException", ex);
                return null;
            }
            catch (SocketException ex)
            {
                Debug.Verbose("HandledException", ex);
                return null;
            }
        }

        /// <summary>
        /// Receive complete message or null. This method blocks current thread until full message is received.
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        [DebuggerStepThrough]
        public static byte[] Receive(this TcpClient client)
        {
            try
            {
                var stream = client.GetStream();

                var bufSize = new byte[4];
                var i = stream.TryRead(bufSize, 0, 4);
                if (i <= 0)
                {
                    return null;
                }
                var size = BitConverter.ToUInt32(bufSize, 0);
                var buf = new byte[size];
                var totalReaded = 0;
                do
                {
                    var received = stream.Read(buf, totalReaded, buf.Length - totalReaded);
                    if (received <= 0)
                    {
                        throw new Exception("Protocol violation or network failure");
                    }
                    totalReaded += received;
                    Debug.Verbose(totalReaded != buf.Length, "NetworkExt: Recieving Segmentation! Readed", "{0} of {1}", totalReaded, buf.Length);
                } while (totalReaded != buf.Length);
                return buf;
            }
            catch (ObjectDisposedException ex)
            {
                Debug.Verbose("HandledException", ex);
                return null;
            }
            catch (SocketException ex)
            {
                Debug.Verbose("HandledException", ex);
                return null;
                //				switch (ex.SocketErrorCode)
                //				{
                //					case SocketError.Interrupted:
                //						return null;
                //					default:
                //						Trace.Exception(ex, "Recieve");
                //						return null;
                //				}
            }
        }
    }
}
